/**
 * 
 */
package com.feib.stms.action;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.stereotype.Service;

import com.feib.stms.dao.BasePaymentFileContentDao;
import com.feib.stms.model.BasePaymentFileContent;
import com.feib.stms.model.Group;
import com.feib.stms.model.Securities;
import com.feib.stms.pagination.PaginatedList;
import com.feib.stms.service.BasePaymentFileContentService;
import com.feib.stms.service.BusinessDayService;
import com.feib.stms.service.GroupService;
import com.feib.stms.util.StmsUtil;
import com.feib.stms.util.UserUtil;

import net.sourceforge.stripes.action.DefaultHandler;
import net.sourceforge.stripes.action.DontBind;
import net.sourceforge.stripes.action.ForwardResolution;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.StreamingResolution;
import net.sourceforge.stripes.integration.spring.SpringBean;
import net.sourceforge.stripes.validation.DateTypeConverter;
import net.sourceforge.stripes.validation.Validate;

/**
 * @author Jimmy Liu
 *
 */
public abstract class BaseQueryUploadFileActionBean<S extends BasePaymentFileContentService<K,T> ,K extends BasePaymentFileContentDao<T>, T extends BasePaymentFileContent> extends BaseActionBean 
{
	private static final long serialVersionUID = 6708556503161518340L;

	/**
	 * 起始頁
	 * @return
	 */
	protected abstract String getDefaultPage();
	
	/**
	 * 明細查詢結果畫面，使用 showModalDialog，供其他程式Link用
	 * @return
	 */
	protected abstract String getShowModalDialogPage();

	/**
	 * 上傳檔案類型
	 * @return
	 */
	public abstract String getUploadFileFormat();

	/**
	 * 下載明細檔名
	 * @return
	 */
	public abstract String getDownloadFileName();

	protected abstract void setPaymentFileContentService(BasePaymentFileContentService<K, T> paymentFileContentService);

	protected DateFormat yyyyMMddHHmmSS = new SimpleDateFormat("yyyyMMddHHmmSS");

	protected BasePaymentFileContentService<K, T> paymentFileContentService;

	protected Class<T> clazzT;

	@SpringBean("messageSource")
	protected ResourceBundleMessageSource messageSource;
	
    @SpringBean("groupService")
    private GroupService groupService;

    
    @SpringBean("businessDayService")
    private  BusinessDayService  businessDayService;
    
	/**
	 * 查詢條件-檔案類型
	 */
	protected List<String> fileFormats;

	/**
	 * 查詢條件
	 */
	protected T qryPymentFileContent;

	/**
	 * 查詢條件-交割日期迄
	 */
	@Validate( converter=DateTypeConverter.class)
	protected Date qryDeliveryDateFrom;

	/**
	 * 查詢條件-交割日期迄
	 */
	@Validate( converter=DateTypeConverter.class)
	protected Date qryDeliveryDateTo;

	/**
	 * 查詢條件-分公司代號
	 */
	protected List<String> qrySecuritiesNos;

	/**
	 * 查詢條件-分公司代號全選 checkbox
	 */
	protected Boolean hideQryResultColumns = false;

	/**
	 * 查詢條件-只能查詢分公司的代號，系統控制
	 */
	protected List<String> userDealSecuritiesNos;

	/**
	 * 查詢結果(多筆)
	 */
	protected PaginatedList<T> queryResult;

	/**
	 * 畫面上券商分公司checkbox
	 */
	protected List<Securities> securities;

	/**
	 * 訊息-畫面顯示用
	 */
	protected String message;

	/**
	 * 錯誤訊息-畫面顯示用
	 */
	protected String errorMessage;

	protected static Map<String, String> SORT_FIELD_NAMES = new LinkedHashMap<String, String>();
	static{
		SORT_FIELD_NAMES.put("accountNo", "ACCOUNT_NO"); // 存款帳號
		SORT_FIELD_NAMES.put("deliveryDate", "DELIVERY_DATE"); // 交割日期或扣帳日期
		SORT_FIELD_NAMES.put("netPay", "NET_PAY"); // 淨付金額
		SORT_FIELD_NAMES.put("netIncome", "NET_INCOME"); // 淨收金額

		SORT_FIELD_NAMES.put("accountNoChk", "ACCOUNT_NO_CHK"); // 帳號檢查碼
		SORT_FIELD_NAMES.put("amountChk", "AMOUNT_CHK"); // 金額檢查碼

		SORT_FIELD_NAMES.put("dealType", "DEAL_TYPE"); // 處理類別
		SORT_FIELD_NAMES.put("customerName", "CUSTOMER_NAME"); // 客戶戶名
		SORT_FIELD_NAMES.put("stockNo", "STOCK_NO"); // 證券代號
		SORT_FIELD_NAMES.put("stockName", "STOCK_NAME"); // 證券名稱



		SORT_FIELD_NAMES.put("chargebackPriority", "CHARGEBACK_PRIORITY"); // 扣款優先種類
		SORT_FIELD_NAMES.put("chargebackPriorityCode", "CHARGEBACK_PRIORITY_CODE"); // 扣款先後序號
		SORT_FIELD_NAMES.put("teller", "TELLER"); // 營業櫃員
		SORT_FIELD_NAMES.put("dealResultCode", "DEAL_RESULT_CODE"); // 處理狀況代號
		SORT_FIELD_NAMES.put("customerId", "CUSTOMER_ID"); // 身份證號
		SORT_FIELD_NAMES.put("securitiesNo", "SECURITIES_NO"); // 券商代號
	}

	@SuppressWarnings("unchecked")
	public BaseQueryUploadFileActionBean()
	{
		clazzT = (Class<T>) ((java.lang.reflect.ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[2];
	}


	@DontBind
	@DefaultHandler
	public Resolution doDefault()
	{
		logger.debug("Enter doDefault mehtod.");
		
		Date nextBusinessDay= businessDayService.getNextBusinessDay(new Date());	
		
		if(null==nextBusinessDay){
			nextBusinessDay=new Date();
		}
				
		qryDeliveryDateFrom = nextBusinessDay;
		qryDeliveryDateTo = nextBusinessDay;
		getQryPymentFileContent();
		
		if (!UserUtil.isBankGroup())
		{
			try {
				qryPymentFileContent = clazzT.newInstance();
				qryPymentFileContent.setGroup(new Group());
				BeanUtils.copyProperties(UserUtil.getUserGroup(), qryPymentFileContent.getGroup(), new String[]{"users", "roles"});
			} catch (Exception e) {
			} 
		}
		return new ForwardResolution(getDefaultPage());
	}

	protected void setQueryCondition(PaginatedList<T> qryCondition, List<String> constrainSecuritiesNos) throws InstantiationException, IllegalAccessException
	{
		logger.debug("Enter setQueryCondition mehtod.");

		/* 設定換頁、排序等條件*/
		String sortCriterion = null == queryResult ? null : queryResult.getSortCriterion();
		List<String> sortFields = getSortFields();
		if (StringUtils.isEmpty(sortCriterion) || null == getSortFieldNames().get(sortCriterion)) // 未設定/找不到 排序條件時候，則以存款帳號為主要排序
			sortCriterion = sortFields.get(0);


		if (null != queryResult)
		{
			qryCondition.setPageNumber(queryResult.getPageNumber());
			qryCondition.setPageSize(queryResult.getPageSize());
			qryCondition.setSortCriterion(getSortFieldNames().get(sortCriterion));
			qryCondition.setSortDirection(queryResult.getSortDirection());
		}


		if (null == queryResult || StringUtils.isEmpty(queryResult.getSortCriterion()) || !sortFields.contains(queryResult.getSortCriterion()))
		{
			// 以存款帳號由大到小排序
			qryCondition.setSortCriterion(getSortFieldNames().get(sortFields.get(0)));
			qryCondition.setSortDirection(PaginatedList.SORT_DIRECTION_DESC);
		}

		if (null == qryPymentFileContent)
			this.qryPymentFileContent = clazzT.newInstance();

		if (null == this.qrySecuritiesNos)
			this.qrySecuritiesNos = new ArrayList<String>();

		if (! UserUtil.isBankGroup())
		{
			// 非銀行端需加入 群組/證券業者 Group 限制
			qryPymentFileContent.setGroup(new Group());
			BeanUtils.copyProperties(UserUtil.getUserGroup(), qryPymentFileContent.getGroup(), new String[]{"users", "roles"});

			/* 加入查詢人員所能查詢的項目的限制   */
			// 由APP_USER中 取得查詢人於可查的分公司代號
			userDealSecuritiesNos = new ArrayList<String>();
			String[] securitiesNos = null;
			if (StringUtils.isNotEmpty(UserUtil.getUser().getDealSecuritiesNos()))
				securitiesNos = UserUtil.getUser().getDealSecuritiesNos().split(","); // 拆解

			if (!ArrayUtils.isEmpty(securitiesNos))
			{
				for(String no : securitiesNos)
				{
					if (StringUtils.isNotEmpty(no))
					{
						userDealSecuritiesNos.add(no);
					}
				}
			}
			else
			{
				// 使用者沒有可查的分公司代號
				constrainSecuritiesNos.add("NO_AUTHORITY");
				return;
			}

			/* 取得交集 */
			final Iterator<String> iterator = userDealSecuritiesNos.iterator();

			while (iterator.hasNext()) {
				final String o = iterator.next();

				if (qrySecuritiesNos.contains(o)) {
					constrainSecuritiesNos.add(o);
				}
			}	
			
			if (CollectionUtils.isEmpty(constrainSecuritiesNos))
			{
				// 畫面上沒輸入任何分公司代號，強迫以使用者可查詢的代號為主
				constrainSecuritiesNos.addAll(userDealSecuritiesNos);
			}		
		}
		else
		{
			constrainSecuritiesNos.addAll(this.qrySecuritiesNos);
		}

	}


	/**
	 * 查詢上傳暫存檔案
	 * @return
	 */
	public Resolution doQuery()
	{
		logger.debug("Enter doQuery mehtod.");
		try {
			
			/* 銀行端一定要輸入群組代號 */
			if (UserUtil.isBankGroup() && "doQuery".equalsIgnoreCase(this.getContext().getEventName()))
			{
				if (null != qryPymentFileContent && null != qryPymentFileContent.getGroup() && null != qryPymentFileContent.getGroup().getGroupNo())
				{
					Group dbGroup = groupService.getByGroupNo(qryPymentFileContent.getGroup().getGroupNo());
					
					if (null == dbGroup)
					{
						this.errorMessage = "券商代號有誤！";
						return new ForwardResolution(getDefaultPage());
					}
					qryPymentFileContent.setGroup(dbGroup);
				}
				else
				{
					this.errorMessage = "請輸入券商代號！";
					return new ForwardResolution(getDefaultPage());
				}
			}
			
			if (null == this.qrySecuritiesNos && "doQuery".equalsIgnoreCase(this.getContext().getEventName())) // 畫面沒選取
			{
				this.errorMessage = "請勾選分公司！";
				return new ForwardResolution(getDefaultPage());
			}

			String sortCriterion = null == queryResult ? null : queryResult.getSortCriterion();
			List<String> sortFields = getSortFields();
			if (StringUtils.isEmpty(sortCriterion) || null == getSortFieldNames().get(sortCriterion)) // 未設定/找不到 排序條件時候，則以存款帳號為主要排序
				sortCriterion = sortFields.get(0);

			List<String> constrainSecuritiesNos = new ArrayList<String>();
			PaginatedList<T> qryCondition = new PaginatedList<T>();
			setQueryCondition(qryCondition, constrainSecuritiesNos);

			this.queryResult = paymentFileContentService.findPaginatedListByPaymentFileContent(this.qryPymentFileContent, this.qryDeliveryDateFrom, this.qryDeliveryDateTo, fileFormats, constrainSecuritiesNos, qryCondition);

			if (null == queryResult)
			{
				this.message = messageSource.getMessage("query.nothing", null, "查無資料！", this.getContext().getRequest().getLocale());
				queryResult = new PaginatedList<T>(); 
				queryResult.setPageNumber(qryCondition.getPageNumber());
				queryResult.setPageSize(qryCondition.getPageSize());
			}

			this.queryResult.setSortCriterion(sortCriterion);
			this.queryResult.setSortDirection(qryCondition.getSortDirection());

		} catch (Exception e) {
			logger.error("查詢發生錯誤:",e);
			this.errorMessage = "因連線問題，請重新再試一次，若仍無法查詢，請聯絡網站管理人員，謝謝！";
		}
		
		return new ForwardResolution(getDefaultPage());
	}
	
	/**
	 * 查詢明細，供其他程式LINK使用
	 * @return
	 */
	public Resolution doQueryWithSowModalDialog()
	{
		logger.debug("Enter doQueryWithSowModalDialog mehtod.");
		try {
			
			doQuery();

		} catch (Exception e) {
			logger.error("查詢發生錯誤:",e);
			this.errorMessage = "因連線問題，請重新再試一次，若仍無法查詢，請聯絡網站管理人員，謝謝！";
		}



		return new ForwardResolution(getShowModalDialogPage());
	}

	/**
	 * 匯出明細表(Excel)
	 * @return
	 */
	public Resolution doExportExcelFile()
	{
		logger.debug("Enter doExportExcelFile mehtod.");

		try {
			
			/* 銀行端一定要輸入群組代號 */
			if (UserUtil.isBankGroup())
			{
				if (null != qryPymentFileContent && null != qryPymentFileContent.getGroup() && null != qryPymentFileContent.getGroup().getGroupNo())
				{
					Group dbGroup = groupService.getByGroupNo(qryPymentFileContent.getGroup().getGroupNo());
					
					if (null == dbGroup)
					{
						this.errorMessage = "券商代號有誤！";
						return new ForwardResolution(getDefaultPage());
					}
					qryPymentFileContent.setGroup(dbGroup);
				}
				else
				{
					this.errorMessage = "請輸入券商代號！";
					return new ForwardResolution(getDefaultPage());
				}
			}

			String downloadFilePath = null; //匯出檔案的完整路徑名稱

			String sortCriterion = null == queryResult ? null : queryResult.getSortCriterion();
			List<String> sortFields = getSortFields();
			if (StringUtils.isEmpty(sortCriterion) || null == getSortFieldNames().get(sortCriterion)) // 未設定/找不到 排序條件時候，則以存款帳號為主要排序
				sortCriterion = sortFields.get(0);

			List<String> constrainSecuritiesNos = new ArrayList<String>();
			PaginatedList<T> qryCondition = new PaginatedList<T>();
			setQueryCondition(qryCondition, constrainSecuritiesNos);

			downloadFilePath = paymentFileContentService.exportExcelByPaymentFileContent(this.qryPymentFileContent, this.qryDeliveryDateFrom, this.qryDeliveryDateTo, fileFormats, constrainSecuritiesNos, qryCondition);

			if (StringUtils.isEmpty(downloadFilePath))
			{
				this.message = messageSource.getMessage("query.nothing", null, "查無資料！", this.getContext().getRequest().getLocale());
				return doQuery();
			}

			File downloadFile = new File(downloadFilePath);
			byte[] bytes = new byte[(int)downloadFile.length()];

			BufferedInputStream bufferedInputStream = 
					new BufferedInputStream(new FileInputStream(downloadFile)); 

			bufferedInputStream.read(bytes);

			ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
			inputStream.close();
			bufferedInputStream.close();

			String isoFileName = new String(getDownloadFileName().getBytes(), "ISO8859-1");

			StreamingResolution streamingResolution = new StreamingResolution( 
					"application/vnd.ms-excel",inputStream).setFilename(isoFileName);

			return streamingResolution;
		} catch (Exception e) {
			logger.error("", e);
		}

		return doQuery(); 
	}

	public List<String> getSortFields()
	{
		List<String> result = new ArrayList<String>();
		CollectionUtils.addAll(result, getSortFieldNames().keySet().iterator()	);
		return result;
	}

	public void setNavigator()
	{
		if (null != queryResult)
		{
			int sortDirection = queryResult.getSortDirection();  //2 : ascending, else: descending
			String sortSpan = "<span class=\"ui-icon ui-icon-circle-triangle-n\"></span>"; //由小到大
			if (1 == sortDirection)
			{
				sortSpan = "<span class=\"ui-icon ui-icon-circle-triangle-s\"></span>"; //由大到小
			}

			String sortCriterion = queryResult.getSortCriterion();
			if (null!= sortCriterion && null != getSortFieldNames().get(sortCriterion))
			{
				this.getContext().getRequest().setAttribute(sortCriterion+"SortedClass", 1==sortDirection?"sortDesc":"sortAsc");
				this.getContext().getRequest().setAttribute(sortCriterion+"SortedSpan", sortSpan);
			}
			else
			{
				sortCriterion = getSortFields().get(0);
				this.getContext().getRequest().setAttribute(sortCriterion+"SortedClass", 1==sortDirection?"sortDesc":"sortAsc");
				this.getContext().getRequest().setAttribute(sortCriterion+"SortedSpan", sortSpan);
			}


			// 上下頁鈕處理
			int lastPageNumber = queryResult.getTotalPageCount();
			int currentPageNumber = queryResult.getPageNumber();
			int prePageNumber = currentPageNumber - 1;
			int nextPageNumber = currentPageNumber + 1;

			this.getContext().getRequest().setAttribute("lastPageNumber", new Integer(lastPageNumber));
			this.getContext().getRequest().setAttribute("currentPageNumber", new Integer(currentPageNumber));
			this.getContext().getRequest().setAttribute("prePageNumber", new Integer(prePageNumber));
			this.getContext().getRequest().setAttribute("nextPageNumber", new Integer(nextPageNumber));
		}
	}

	/**
	 * 只顯示使用者可以查詢的母/分公司
	 * @return
	 */
	public List<Securities> getSecurities() {
		if (null == securities)
		{
			if (UserUtil.isBankGroup())
			{
				if (null != qryPymentFileContent && null != qryPymentFileContent.getGroup() && null != qryPymentFileContent.getGroup().getGroupPk())
					securities = StmsUtil.getInstance().getSecuritiesByGroupPk(qryPymentFileContent.getGroup().getGroupPk());
			}
			else
			{
				securities = new ArrayList<Securities>();
				List<Securities> tempSecurities = StmsUtil.getInstance().getSecuritiesByGroupPk(UserUtil.getUserGroup().getGroupPk());

				/* 加入查詢人員所能查詢的項目的限制   */
				// 由APP_USER中 取得查詢人於可查的分公司代號
				userDealSecuritiesNos = new ArrayList<String>();
				String[] securitiesNos = null;
				if (StringUtils.isNotEmpty(UserUtil.getUser().getDealSecuritiesNos()))
					securitiesNos = UserUtil.getUser().getDealSecuritiesNos().split(","); // 拆解

				if (!ArrayUtils.isEmpty(securitiesNos))
				{
					for(String no : securitiesNos)
					{
						if (StringUtils.isNotEmpty(no))
						{
							userDealSecuritiesNos.add(no);
						}
					}
				}

				/* 取得交集 */
				final Iterator<String> iterator = userDealSecuritiesNos.iterator();

				while (iterator.hasNext()) {
					final String o = iterator.next();

					for(Securities s: tempSecurities)
					{
						if (s.getSecuritiesNo().equalsIgnoreCase(o))
						{
							securities.add(s);
							break;
						}
					}		        
				}

				tempSecurities.clear();
				tempSecurities = null;
			}

		}

		return securities;
	}

	public static Map<String, String> getSortFieldNames(){
		return SORT_FIELD_NAMES;
	}

	public Date getQryDeliveryDateFrom() {
		return qryDeliveryDateFrom;
	}

	public void setQryDeliveryDateFrom(Date qryDeliveryDateFrom) {
		this.qryDeliveryDateFrom = qryDeliveryDateFrom;
	}

	public Date getQryDeliveryDateTo() {
		return qryDeliveryDateTo;
	}

	public void setQryDeliveryDateTo(Date qryDeliveryDateTo) {
		this.qryDeliveryDateTo = qryDeliveryDateTo;
	}

	public BasePaymentFileContent getQryPymentFileContent() {
		
		if (null == qryPymentFileContent)
		{
			try {
				qryPymentFileContent = clazzT.newInstance();
			} catch (InstantiationException e) {
			} catch (IllegalAccessException e) {
			} 
		}
		if (! UserUtil.isBankGroup())
		{
			// 非銀行端需加入 群組/證券業者 Group 限制
			qryPymentFileContent.setGroup(new Group());
			BeanUtils.copyProperties(UserUtil.getUserGroup(), qryPymentFileContent.getGroup(), new String[]{"users", "roles"});
		}
		return qryPymentFileContent;
	}

	public void setQryPymentFileContent(T qryPymentFileContent) {
		this.qryPymentFileContent = qryPymentFileContent;
	}

	public List<String> getQrySecuritiesNos() {
		return qrySecuritiesNos;
	}

	public void setQrySecuritiesNos(List<String> qrySecuritiesNos) {
		this.qrySecuritiesNos = qrySecuritiesNos;
	}

	public PaginatedList<? extends BasePaymentFileContent> getQueryResult() {
		return queryResult;
	}

	public void setQueryResult(	PaginatedList<T> queryResult) {
		this.queryResult = queryResult;
	}

	public String getMessage() {
		return message;
	}

	public ResourceBundleMessageSource getMessageSource() {
		return messageSource;
	}

	public String getErrorMessage() {
		return errorMessage;
	}

	public Boolean getHideQryResultColumns() {
		return hideQryResultColumns;
	}

	public void setHideQryResultColumns(Boolean hideQryResultColumns) {
		this.hideQryResultColumns = hideQryResultColumns;
	}


}
